//package com.atguigu.tingshu.common.login;
//
//import com.atguigu.tingshu.common.constant.RedisConstant;
//import com.atguigu.tingshu.common.execption.GuiguException;
//import com.atguigu.tingshu.common.result.ResultCodeEnum;
//import com.atguigu.tingshu.common.util.AuthContextHolder;
//import com.atguigu.tingshu.model.user.UserInfo;
//
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletResponse;
//import org.aspectj.lang.ProceedingJoinPoint;
//import org.aspectj.lang.annotation.Around;
//import org.aspectj.lang.annotation.Aspect;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.dao.PessimisticLockingFailureException;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.stereotype.Component;
//import org.springframework.util.StringUtils;
//import org.springframework.web.context.request.RequestAttributes;
//import org.springframework.web.context.request.RequestContextHolder;
//import org.springframework.web.context.request.ServletRequestAttributes;
//
//@Component
//@Aspect
//public class TsLoginAspect {
//    @Autowired
//    private RedisTemplate redisTemplate;
//    //环绕通知
//    @Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(tsLogin)")
//    public Object doTsLoginAspect(ProceedingJoinPoint point,TsLogin tsLogin) throws Throwable {
//        //判断是否登录
//        //获取请求信息
//        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//        //转换成servletrequestattributes
//        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
//        //获取Http请求对象
//        HttpServletRequest request = servletRequestAttributes.getRequest();
//        //获取响应对象
//        //HttpServletResponse response = servletRequestAttributes.getResponse();
//
//
//        //获取token
//        String token = request.getHeader("token");
//        //判断注解属性，true说明当前业务是需要登录才能进行的
//        try {
//            if(tsLogin.required()){
//                //当前业务需要登录才能进行
//                //判断是否已经登录，就需要看token是否为空，如果为空，说明没有登录，需要跳转到登录页面
//                if(StringUtils.isEmpty(token)){
//                    //说明没有登录，需要跳转到登录页面
//                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
//                }
//                //登录了，但是token是否正确，需要从redis中获取token，判断是否一致，如果不一致，说明token已经过期，需要重新登录
//                String userLoginToken = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
//                UserInfo userInfo = (UserInfo)redisTemplate.opsForValue().get(userLoginToken);
//
//                if(null == userInfo){
//                    //说明没有登录，需要跳转到登录页面
//                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
//                }
//            }
//
//            //说明业务不需要进行登录,还想获取用户信息，只能是从缓存中去获取数据了
//            if(!StringUtils.isEmpty(token)){
//                //从缓存中拿数据
//                String userLoginToken = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
//                UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(userLoginToken);
//                if(null != userInfo){
//                    //存储用户id到本地线程中
//                    AuthContextHolder.setUserId(userInfo.getId());
//                }
//            }
//            //注释放行，执行目标方法
//            return point.proceed();
//        } finally {
//            AuthContextHolder.removeUserId();
//        }
//
//        /*//如何判断用户是登录的，需要使用token来看是否为登录状态
//        //如果请求头中有token，就能获取到用户信息，否则就是未登录
//        //1. todo 获取请求信息
//        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//        //转换成servletrequestattributes,获取请求对象
//        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
//        //获取请求对象
//        HttpServletRequest request = servletRequestAttributes.getRequest();
//        //获取请求信息中请求头的token
//        String token = request.getHeader("token");
//        //先判断注解属性，true说明当前业务是需要登录才能进行的
//        if(tsLogin.required()){
//           //当前业务需要登录才能进行
//            if (StringUtils.isEmpty(token)){
//                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
//            }
//            //设置token
//            String userLoginToken = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
//            //根据token获取用户信息
//            UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(userLoginToken);
//            //判断用户信息是否为空
//            if(null == userInfo){
//                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
//            }
//        }
//        //未登录但也想拿到数据
//        if(!StringUtils.isEmpty(token)){
//            //从缓存中拿数据
//            String userLoginToken = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
//            UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(userLoginToken);
//            if(null != userInfo){
//                //存储用户id到本地线程中
//                AuthContextHolder.setUserId(userInfo.getId());
//            }
//        }
//        return point.proceed();*/
//    }
//}
package com.atguigu.tingshu.common.login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.UUID;

/**
 * @author atguigu-mqx
 * @ClassName TsLoginAspect
 * @description: TODO
 * @date 2024年11月23日
 * @version: 1.0
 */
@Component
@Aspect
public class TsLoginAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    //   @annotation(org.springframework.transaction.annotation.Transactional)
    //  环绕通知：切注解TsLogin 这个注解;
    /*@Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(tsLogin)")
    public Object doTsLoginAspect(ProceedingJoinPoint point,TsLogin tsLogin) throws Throwable {
        Object obj = new Object();
        //  如何判断用户是否登录的?
        *//*
        1.  需要通过token 来判断用户是否登录!
            如果请求头中有token，则说明登录！否则，没有登录！
         *//*
        //  获取到请求头中的 token
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        //  获取的请求对象
        HttpServletRequest request = servletRequestAttributes.getRequest();
        //  获取到响应对象
        //  HttpServletResponse response = servletRequestAttributes.getResponse();
        String token = request.getHeader("token");
        //  判断注解的属性：
        if (tsLogin.required()){
            //  判断token 是否为空
            if (StringUtils.isEmpty(token)){
                //  抛出异常
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
            //  根据token 从缓存(redis)中获取用户信息数据; user_info==UserInfo;
            String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);
            //  判断
            if (null == userInfo){
                //  抛出异常
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
        }
        try {
            //  说明此处不需要登录.
            if (!StringUtils.isEmpty(token)){
                //  从缓存中获取数据
                String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);
                if (null != userInfo){
                    //  存储 用户Id 到本地线程
                    AuthContextHolder.setUserId(userInfo.getId());
                }
            }
            //  执行方法体
            return point.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            //  释放本地内存，防止ThreadLocal 内存泄漏！
            AuthContextHolder.removeUserId();
        }
    }*/
    @Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(tsLogin)")
    public Object login( ProceedingJoinPoint joinPoint,TsLogin tsLogin) throws Throwable {
        try {
            //  需要判断当前这个用户是否登录！ 一处登录，处处使用 --- 单点登录SSO; 1. 用户名，密码；2. 微信小程序登录 3. 第三方授权 4. 手机验证码
            //  登录过程： 利用微信小程序登录接口--通过前端传递的code码；调用api能够获取 openId【每个人的微信都有一个唯一的】;
            //  查询用户表；有登录成功-还会将这个用户信息保存到redis，没有注册！
            //  登录成功： 会生成一个token；token就是一个uuid; 将这个token返回给前端；并将这个token 保存到请求头;
            //  重点判断请求头中是否有token就可以了！前端还会将token存储到浏览器本地缓存中！ *****
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();
            //  获取到token数据.
            String token = request.getHeader("token");
            //  编写用户在访问需要登录的业务时，没有登录，则跳转到登录业务逻辑.
            if (tsLogin.required()) {
                //  必须要登录！ 如何没有登录。则跳转!
                if (StringUtils.isEmpty(token)){
                    //  说明没有登录; 则抛出一个异常，页面根据这个异常就会跳转到登录页面！
                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
                }
                //  token 不为空，则说明登录过！  还会将这个用户信息保存到redis
                //  此时需要进一步判断缓存中是否有用户信息,如果缓存中没有也不能说明登录！
                //  考虑登录时，缓存的key=token value=userInfo;
                String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
                //  获取缓存中用户数据
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(loginKey);
                if (null == userInfo){
                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
                }
            }

            //  只要满足token 不为空的时候。就做一件事！将用户Id 存储到本地线程中！ 便于后台微服务获取用户Id！
            if (!StringUtils.isEmpty(token)){
                //  组成缓存key
                String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
                //  获取缓存中用户数据
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(loginKey);
                if (null != userInfo){
                    //  存储用户Id
                    AuthContextHolder.setUserId(userInfo.getId());
                }
            }
            //  执行方法体;
            return joinPoint.proceed();
        } finally {
            //  删除本地数据
            AuthContextHolder.removeUserId();
        }
    }

}
